package test.example.auth;

import java.io.IOException;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slim3.datastore.Datastore;
import org.slim3.memcache.Memcache;

import test.example.model.AccessToken;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gdata.client.authn.oauth.GoogleOAuthHelper;
import com.google.gdata.client.authn.oauth.GoogleOAuthParameters;
import com.google.gdata.client.authn.oauth.OAuthException;
import com.google.gdata.client.authn.oauth.OAuthHmacSha1Signer;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.util.AuthenticationException;

public class GoogleConnection {

    private DocsService myService = new DocsService("My Application");
    private String account;
    private String pass;

    private String CONSUMER_KEY = "gdocgroopacl.appspot.com";
    private String CONSUMER_SECRET = "kZx9BttHN2QdOvYIhx-Kt8EJ";

    private static GoogleConnection instance = new GoogleConnection();

    public static GoogleConnection getInstance() {
        return instance;
    }

    public void init(String account, String pass) {
        this.setAccount(account);
        this.setPass(pass);
    }

    private GoogleConnection() {
    }

    public void connectionStart(HttpServletRequest request,
            HttpServletResponse response) throws OAuthException, IOException {
        UserService userService = UserServiceFactory.getUserService();
        String mailAddress = userService.getCurrentUser().getEmail();
        AccessToken acPOPOPOPOON = restoreAccessToken();
        if (acPOPOPOPOON == null) {

            if (request.getParameter("oauth_token") == null) {
                // oauth取得のために、未承認のリクエストトークンを取得し、認証URLの作成およびリダイレクトを行う。
                GoogleOAuthParameters oauthParameters =
                    new GoogleOAuthParameters();
                oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
                oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
                oauthParameters.setOAuthSignatureMethod("HMAC-SHA1");
                oauthParameters.setScope("https://docs.google.com/feeds/");

                GoogleOAuthHelper oauthHelper =
                    new GoogleOAuthHelper(new OAuthHmacSha1Signer());
                oauthHelper.getUnauthorizedRequestToken(oauthParameters);

                String tokenSecret = oauthParameters.getOAuthTokenSecret();

                if (request.getServerName().equals("localhost")) {
                    oauthParameters.setOAuthCallback(String.format(
                        "http://localhost:8888/main?secret=%s",
                        URLEncoder.encode(tokenSecret, "UTF-8")));
                }
                // App Engineの場合
                else {
                    oauthParameters.setOAuthCallback(String.format(
                        "http://localhost:8080/main?secret=%s",
                        URLEncoder.encode(tokenSecret, "UTF-8")));
                }
                String approvalPageUrl =
                    oauthHelper.createUserAuthorizationUrl(oauthParameters);
                response.sendRedirect(approvalPageUrl);
                // ここでリダイレクト処理をするとコントローラーでフォワード出来ずに警告が出るが、こちらが優先されるため動作に問題はない。

                return;
            } else if (request.getParameter("oauth_token") != null) {
                // 認証済みリクエストトークンの処理
                // oauth_token_secretをURLから取得し、oauthParametersに格納する
                GoogleOAuthParameters oauthParameters =
                    new GoogleOAuthParameters();
                oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
                oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
                oauthParameters.setOAuthTokenSecret(request
                    .getParameter("secret"));

                GoogleOAuthHelper helper =
                    new GoogleOAuthHelper(new OAuthHmacSha1Signer());
                helper
                    .getAccessToken(request.getQueryString(), oauthParameters);

                // accessTokenを取得する。
                String accessToken = oauthParameters.getOAuthToken();
                // accessTokenSecretを取得する。
                String accessTokenSecret =
                    oauthParameters.getOAuthTokenSecret();

                // Memcache+Datastoreへの格納
                this.storeAccessToken(accessToken, accessTokenSecret);
                this.connectionStart(request, response);
            }
        } else {
            // 取得したaccessToken,accessTokenSecretで接続を開始する。
            GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
            oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
            oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);

            oauthParameters.setOAuthToken(acPOPOPOPOON.getAccessToken());
            oauthParameters.setOAuthTokenSecret(acPOPOPOPOON
                .getAccessTokenSecret());
            myService.setOAuthCredentials(
                oauthParameters,
                new OAuthHmacSha1Signer());
        }
    }

    private void storeAccessToken(String accessToken, String accessTokenSecret) {
        UserService us = UserServiceFactory.getUserService();
        String mailAddress = us.getCurrentUser().getEmail();

        Key key = Datastore.createKey(AccessToken.class, mailAddress);

        AccessToken entity = new AccessToken();
        entity.setKey(key);
        entity.setAccessToken(accessToken);
        entity.setAccessTokenSecret(accessTokenSecret);

        Memcache.put(key, entity);
        Datastore.put(entity);
    }

    private AccessToken restoreAccessToken() {
        UserService us = UserServiceFactory.getUserService();
        String mailAddress = us.getCurrentUser().getEmail();

        Key key = Datastore.createKey(AccessToken.class, mailAddress);

        AccessToken entity = Memcache.get(key);
        if (entity != null
            && entity.getAccessToken() != null
            && entity.getAccessTokenSecret() != null) {
            return entity;
        }
        return Datastore.getOrNull(AccessToken.class, key);
    }

    public boolean connectionStart() throws AuthenticationException {
        myService.setUserCredentials(account, pass);
        return true;
    }

    public DocsService getMyService() {
        return myService;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public void setPass(String pass) {
        this.pass = pass;
    }

    public void connectionStop(HttpServletRequest request,
            HttpServletResponse response) {
        UserService userService = UserServiceFactory.getUserService();
        String mailAddress = userService.getCurrentUser().getEmail();
        Memcache.delete("accessToken::" + mailAddress);
        Memcache.delete("accessTokenSecret::" + mailAddress);
        return;
    }

}
